﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Elderos.Dialogs;
using Elderos.Utils;
using FontDialogSample;
using Microsoft.Samples.CustomControls;
using TreeBuilder.Settings;

namespace TreeBuilder
{
	/// <summary>
	/// Interaction logic for TreeNode.xaml
	/// </summary>
	public partial class TreeNode : UserControl, INode, IMenuChangeableObject
	{
		private bool _textWasChanged;

		public IndexBlock UpperIndexAdorner;
		public IndexBlock LowerIndexAdorner;

		private readonly List<IAdorner> _adorners = new List<IAdorner>();

		public event Action<object, FontChangedEventArgs> FontChanged;

		protected virtual void OnFontChanged(object sender, FontChangedEventArgs e)
		{
			Action<object, FontChangedEventArgs> handler = FontChanged;
			if (handler != null) handler(sender, e);
		}

		public event Action<object, DragInitEventArgs> DragInit;

		protected virtual void OnDragInit(object sender, DragInitEventArgs args)
		{
			Action<object, DragInitEventArgs> handler = DragInit;
			if (handler != null) handler(sender, args);
		}

		#region Implementation of IBindable
		private readonly List<IBinder> _links = new List<IBinder>();

		public List<IBinder> Links
		{
			get { return _links; }
		}

		public bool IsBeingLinked
		{
			get { return (bool)GetValue(IsBeingLinkedProperty); }
			set { SetValue(IsBeingLinkedProperty, value); }
		}

		public static readonly DependencyProperty IsBeingLinkedProperty =
			DependencyProperty.Register("IsBeingLinked", typeof(bool), typeof(TreeNode), new UIPropertyMetadata(false));

		public event Action<object, EventArgs> Delete;

		protected virtual void OnDelete(object sender)
		{
			Action<object, EventArgs> handler = Delete;
			if (handler != null) handler(sender, EventArgs.Empty);
		}


		public event Action<object, InitBindingEventArgs> InitBinding; 

		protected virtual void OnInitBinding(object sender, InitBindingEventArgs e)
		{
			Action<object, InitBindingEventArgs> handler = InitBinding;
			if (handler != null) handler(sender, e);
		}

		public event Action<object, CompleteBindingEventArgs> CompleteBinding;

		protected virtual void OnCompleteBinding(object sender, CompleteBindingEventArgs e)
		{
			Action<object, CompleteBindingEventArgs> handler = CompleteBinding;
			if (handler != null) handler(sender, e);
		}
		#endregion

		public bool IsBeingDragged
		{
			get { return (bool)GetValue(IsBeingDraggedProperty); }
			set
			{
				SetValue(IsBeingDraggedProperty, value);
				if (value)
				{
					foreach (var btn in ButtonPanel.Children.OfType<Button>())
						btn.Opacity = 0.1;
					TextBox.Opacity = 0.5;
				}
				else
				{
					foreach (var btn in ButtonPanel.Children.OfType<Button>())
						btn.Opacity = 1;
					TextBox.Opacity = 1;
				}
			}
		}

		// Using a DependencyProperty as the backing store for IsBeingDragged.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty IsBeingDraggedProperty =
			DependencyProperty.Register("IsBeingDragged", typeof(bool), typeof(TreeNode), new UIPropertyMetadata(false));



		public Point? Offset { get; set; }

		private Guid _guid = Guid.NewGuid();

		public Guid Guid
		{
			get { return _guid; }
			set { _guid = value; }
		}

		public Rect Location
		{
			get
			{
				double x = Canvas.GetLeft(this);
				double y = Canvas.GetTop(this) + ButtonPanel.ActualHeight;
				double width = TextBox.ActualWidth;
				double height = TextBox.ActualHeight;
				return new Rect(x, y, width, height);
			}
		}

		public Point TopCenter
		{
			get { return (Point)GetValue(TopCenterProperty); }
			set
			{
				//avoiding to cross the left border of canvas
				double textBoxWidth = TextBox.ActualWidth;
				double width = this.ActualWidth;

				double horizontalOffset = Math.Max(textBoxWidth, width) / 2;

				double x = Math.Max(value.X, 0 + horizontalOffset);
				double y = Math.Max(value.Y, 0 + ButtonPanel.ActualHeight);

				SetValue(TopCenterProperty, new Point(x, y));
				OnTopCenterChanged();
			}
		}

		// Using a DependencyProperty as the backing store for TopCenter.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty TopCenterProperty =
			DependencyProperty.Register("TopCenter", typeof(Point), typeof(TreeNode), new UIPropertyMetadata(default(Point)));

		public virtual void OnTopCenterChanged()
		{
			//fixed: when a coordinate was less than 0 printing to image crashed
			Canvas.SetLeft(this, TopCenter.X - ActualWidth/2);
			Canvas.SetTop(this, Math.Max(TopCenter.Y, 0) - ButtonPanel.ActualHeight);

			_links.ForEach(x => x.RefreshPoints());
			_adorners.ForEach(x => x.RefreshPoints());
		}

		public double ButtonPanelHeight
		{
			get { return ButtonPanel.ActualHeight; }
		}

		private bool _needsCanvasAlignment = true;
		public bool NeedsCanvasAlignment
		{
			get { return _needsCanvasAlignment; }
			set { _needsCanvasAlignment = value; }
		}

		public Point Center
		{
			get
			{
				double x = TopCenter.X;
				double y = Canvas.GetTop(this) + ButtonPanel.ActualHeight;
				return new Point(x, y);
			}
		}

		public Point BottomCenter
		{
			get
			{
				double x = TopCenter.X;
				double y = Canvas.GetTop(this) + this.ActualHeight;
				return new Point(x, y);
			}
		}

		public Point RightCenter
		{
			get
			{
				double x = Canvas.GetLeft(this) + Math.Max(TextBox.ActualWidth, ButtonPanel.ActualWidth);
				if (ButtonPanel.ActualWidth > TextBox.ActualWidth)
					x -= (ButtonPanel.ActualWidth - TextBox.ActualWidth)/2;
				double y = Canvas.GetTop(this) + ButtonPanel.ActualHeight + TextBox.ActualHeight / 2;
				return new Point(x, y);
			}
		}

		public Point LeftCenter
		{
			get
			{
				double x = Canvas.GetLeft(this);
				if (ButtonPanel.ActualWidth > TextBox.ActualWidth)
					x += (ButtonPanel.ActualWidth - TextBox.ActualWidth)/2;
				double y = Canvas.GetTop(this) + ButtonPanel.ActualHeight + TextBox.ActualHeight / 2;
				return new Point(x, y);
			}
		}

		public event Action<object, SelectionEventArgs> Selected;

		protected virtual void OnSelected(object sender, SelectionEventArgs e)
		{
			Action<object, SelectionEventArgs> handler = Selected;
			if (handler != null) handler(sender, e);
		}

	    public bool Intersects(Rect selectionRect)
	    {
	        return this.Location.IntersectsWith(selectionRect);
	    }

	    public bool IsSelected
		{
			get { return (bool)GetValue(IsSelectedProperty); }
			set
			{
				SetValue(IsSelectedProperty, value);
				InvalidateAdorners();
			}
		}

		public static readonly DependencyProperty IsSelectedProperty =
			DependencyProperty.Register("IsSelected", typeof(bool), typeof(TreeNode), new PropertyMetadata(false));

		public TreeNode()
		{
			InitializeComponent();

			Initialize(
				NodeSettings.Default.FontColor,
				NodeSettings.Default.FontFamily ?? FontFamily,
				NodeSettings.Default.FontSize,
				NodeSettings.Default.FontStretch,
				NodeSettings.Default.FontStyle,
				NodeSettings.Default.FontWeight,
				NodeSettings.Default.TextDecorations
				);

			InitializeAdorners();

			_textWasChanged = false;
		}

		private void InitializeAdorner(ref IndexBlock indexBlock, IndexTypes indexType, Color fontColor, FontFamily fontFamily, 
			double fontSize, FontStretch fontStretch, FontStyle fontStyle, 
			FontWeight fontWeight, TextDecorationCollection textDecorations)
		{
			indexBlock = CreateIndex(indexType,
									 new SolidColorBrush(fontColor), fontFamily, fontSize,
									 fontStretch, fontStyle, fontWeight, textDecorations);
			indexBlock.Element = this;
			_adorners.Add(indexBlock);
		}

		private void InitializeAdorner(ref IndexBlock indexBlock, IndexTypes indexType)
		{
			switch (indexType)
			{
				case IndexTypes.Upper:
					InitializeAdorner(ref indexBlock, indexType, IndexSettings.Default.UpperFontColor,
									  IndexSettings.Default.UpperFontFamily ?? FontFamily,
									  IndexSettings.Default.UpperFontSize,
									  IndexSettings.Default.UpperFontStretch,
									  IndexSettings.Default.UpperFontStyle,
									  IndexSettings.Default.UpperFontWeight,
									  IndexSettings.Default.UpperTextDecorations);
					break;
				case IndexTypes.Lower:
					InitializeAdorner(ref indexBlock, indexType, IndexSettings.Default.LowerFontColor,
									  IndexSettings.Default.LowerFontFamily ?? FontFamily,
									  IndexSettings.Default.LowerFontSize,
									  IndexSettings.Default.LowerFontStretch,
									  IndexSettings.Default.LowerFontStyle,
									  IndexSettings.Default.LowerFontWeight,
									  IndexSettings.Default.LowerTextDecorations);
					break;
				default:
					InitializeAdorner(ref indexBlock, indexType, IndexSettings.Default.UpperFontColor,
									  IndexSettings.Default.UpperFontFamily ?? FontFamily,
									  IndexSettings.Default.UpperFontSize,
									  IndexSettings.Default.UpperFontStretch,
									  IndexSettings.Default.UpperFontStyle,
									  IndexSettings.Default.UpperFontWeight,
									  IndexSettings.Default.UpperTextDecorations);
					break;
			}


		}

		private void InitializeAdorners()
		{
			InitializeAdorner(ref UpperIndexAdorner, IndexTypes.Upper);
			InitializeAdorner(ref LowerIndexAdorner, IndexTypes.Lower);
		}

		private IndexBlock CreateIndex(IndexTypes type,
			Brush brush, FontFamily fontFamily, double fontSize,
			FontStretch fontStretch, FontStyle fontStyle,
			FontWeight fontWeight, TextDecorationCollection decorations)
		{
			var index = new IndexBlock {IndexType = type};

			Font.ApplyFont(index.TextBlock, brush, fontFamily, fontSize,
				fontStretch, fontStyle, fontWeight, decorations);
			index.TextBlock.Text = "";

			return index;
		}

		private void Initialize(Color fontColor, FontFamily fontFamily, double fontSize,
			FontStretch fontStretch, FontStyle fontStyle,
			FontWeight fontWeight, TextDecorationCollection textDecorations)
		{
			Font.ApplyFont(TextBox, new SolidColorBrush(fontColor), fontFamily, fontSize,
						   fontStretch, fontStyle, fontWeight, textDecorations);
		}

		protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
		{
			e.Handled = true;
		}

		public event Action<object, NodeTextChangedEventArgs> TextChanged;

		protected virtual void OnTextChanged(object sender, NodeTextChangedEventArgs args)
		{
			Action<object, NodeTextChangedEventArgs> handler = TextChanged;
			if (handler != null) handler(sender, args);
		}

		private string _previousText = "";

		private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
		{
			TopCenter = TopCenter;
			_textWasChanged = true;
		}

		private void DragButton_MouseDown(object sender, MouseButtonEventArgs e)
		{
			OnDragInit(sender, new DragInitEventArgs(this, e.GetPosition((UIElement)Parent)));
			DragButton.Focus();
			e.Handled = true;
		}

		private void Node_GotFocus(object sender, RoutedEventArgs e)
		{
			ButtonPanel.Visibility = Visibility.Visible;

		    if (!IsSelected)
		    {
		        OnSelected(sender, new SelectionEventArgs(this));
		    }
		}

		private void Node_LostFocus(object sender, RoutedEventArgs e)
		{
			if (!ButtonPanel.IsMouseOver && !DragButton.IsFocused)
				ButtonPanel.Visibility = Visibility.Hidden;
		}

		private void InvalidateAdorners()
		{
			var adornerLayer = AdornerLayer.GetAdornerLayer(TextBox);
			if (adornerLayer == null) return;

			var adorners = adornerLayer.GetAdorners(TextBox);
			if (adorners == null) return;

			foreach (var adorner in adorners)
			{
				adorner.InvalidateVisual();
			}
		}

		private void TextBox_DragOver(object sender, DragEventArgs e)
		{
			OnDragOver(e);
		}

		private void TextBox_PreviewDrop(object sender, DragEventArgs e)
		{
			e.Handled = true;
		}

		private void FontButton_MouseUp(object sender, MouseButtonEventArgs e)
		{
			var fontChooser = new FontChooser();
			Font.ApplyFont(fontChooser,
				TextBox.Foreground,
				TextBox.FontFamily,
				TextBox.FontSize,
				TextBox.FontStretch,
				TextBox.FontStyle,
				TextBox.FontWeight,
				TextBox.TextDecorations
				);


			if (fontChooser.ShowDialog() != true) return;

			var font = Font.ExtractFont(fontChooser);
			ChangeFont(font);
		}

	    public void ChangeFont(Font font)
	    {
	        OnFontChanged(null, new FontChangedEventArgs(font));
	    }

		private void ButtonPanel_MouseLeave(object sender, MouseEventArgs e)
		{
			//if (!this.IsFocused)
			//	ButtonPanel.Visibility = Visibility.Hidden;
		}

		private void DeleteButton_Click(object sender, RoutedEventArgs e)
		{
			OnDelete(sender);
		}

		private void LineButton_Click(object sender, RoutedEventArgs e)
		{
			OnInitBinding(sender, new InitBindingEventArgs(() => new LineLink()));
		}

		private void TextBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			OnCompleteBinding(sender, new CompleteBindingEventArgs(this));
			if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
			{
				OnSelected(sender, new SelectionEventArgs(this));
				e.Handled = true;
				return;
			}
			TextBox.Focus();
		}

		private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			TopCenter = TopCenter;
			OnTopCenterChanged();
		}

		private void ArrowButton_Click(object sender, RoutedEventArgs e)
		{
			OnInitBinding(sender, new InitBindingEventArgs(() => new ArrowLink()));
		}

		#region Implementation of IXmlSerializable

		/// <summary>
		/// This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method.
		/// </returns>
		public XmlSchema GetSchema()
		{
			return null;
		}

		/// <summary>
		/// Generates an object from its XML representation.
		/// </summary>
		/// <param name="reader">The <see cref="T:System.Xml.XmlReader"/> stream from which the object is deserialized. </param>
		public void ReadXml(XmlReader reader)
		{
			XDocument xdoc = XDocument.Load(reader);

			var root = xdoc.Root;

			TextBox.Text = root.Element("Text").Value;
			TextBox.FontFamily = (FontFamily)XamlReader.Parse(root.Element("FontFamily").Value);
			TextBox.FontSize = double.Parse(root.Element("FontSize").Value.Replace(",", "."), CultureInfo.InvariantCulture);
			TextBox.FontStretch = (FontStretch)XamlReader.Parse(root.Element("FontStretch").Value);
			TextBox.FontStyle = (FontStyle)XamlReader.Parse(root.Element("FontStyle").Value);
			TextBox.FontWeight = (FontWeight)XamlReader.Parse(root.Element("FontWeight").Value);
			TextBox.TextDecorations =(TextDecorationCollection)XamlReader.Parse(root.Element("TextDecorations").Value);
			TextBox.Foreground = (Brush)XamlReader.Parse(root.Element("Foreground").Value);

			if (root.Element("UpperIndex") == null)
			{
				InitializeAdorner(ref UpperIndexAdorner, IndexTypes.Upper);
			}
			else
			{
				UpperIndexAdorner = XmlSerializationUtility.Deserialize<IndexBlock>(root.Element("UpperIndex").Value);
				_adorners.Add(UpperIndexAdorner);
				UpperIndexAdorner.Element = this;
			}

			if (root.Element("LowerIndex") == null)
				InitializeAdorner(ref LowerIndexAdorner, IndexTypes.Lower);
			else
			{
				LowerIndexAdorner = XmlSerializationUtility.Deserialize<IndexBlock>(root.Element("LowerIndex").Value);
				_adorners.Add(LowerIndexAdorner);
				LowerIndexAdorner.Element = this;
			}


			TopCenter = XmlSerializationUtility.Deserialize<Point>(root.Element("TopCenter").Value);
			Guid = XmlSerializationUtility.Deserialize<Guid>(root.Element("Guid").Value);
		}

		/// <summary>
		/// Converts an object into its XML representation.
		/// </summary>
		/// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/> stream to which the object is serialized. </param>
		public void WriteXml(XmlWriter writer)
		{
			writer.WriteRaw("<Text xml:space=\"preserve\">" + TextBox.Text + "</Text>");
			writer.WriteElementString("FontFamily", XamlWriter.Save(TextBox.FontFamily));
			writer.WriteElementString("FontSize", TextBox.FontSize.ToString());
			writer.WriteElementString("FontStretch", XamlWriter.Save(TextBox.FontStretch));
			writer.WriteElementString("FontStyle", XamlWriter.Save(TextBox.FontStyle));
			writer.WriteElementString("FontWeight", XamlWriter.Save(TextBox.FontWeight));
			writer.WriteElementString("TextDecorations", XamlWriter.Save(TextBox.TextDecorations));
			writer.WriteElementString("Foreground", XamlWriter.Save(TextBox.Foreground));

			SerializeIndex(writer, "UpperIndex", UpperIndexAdorner);
			SerializeIndex(writer, "LowerIndex", LowerIndexAdorner);

			writer.WriteElementString("TopCenter", XmlSerializationUtility.Serialize(TopCenter));
			writer.WriteElementString("Guid", XmlSerializationUtility.Serialize(Guid));
		}

		private static void SerializeIndex(XmlWriter writer, string name, IndexBlock index)
		{
			if (String.IsNullOrEmpty(index.TextBlock.Text)) return;

			var element = new XElement(name);
			element.Add(
				XmlSerializationUtility.Serialize(index)
				);

			writer.WriteRaw(element.ToString());
		}

		#endregion

		#region Implementation of IPrintable

		public bool IsBeingPrinted
		{
			get { return (bool)GetValue(IsBeingPrintedProperty); }
			set { SetValue(IsBeingPrintedProperty, value); }
		}

		// Using a DependencyProperty as the backing store for IsBeingPrinted.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty IsBeingPrintedProperty =
			DependencyProperty.Register("IsBeingPrinted", typeof(bool), typeof(TreeNode), new UIPropertyMetadata(false));

		#endregion

		private void TriangleButton_Click(object sender, RoutedEventArgs e)
		{
			OnInitBinding(this, new InitBindingEventArgs(() => new TriangleLink()));
		}

		private void UpperIndexFontBtn_Click(object sender, RoutedEventArgs e)
		{
			var fontChooser = new FontChooser();
			Font.ApplyFont(fontChooser,
				UpperIndexAdorner.TextBlock.Foreground,
				UpperIndexAdorner.TextBlock.FontFamily,
				UpperIndexAdorner.TextBlock.FontSize,
				UpperIndexAdorner.TextBlock.FontStretch,
				UpperIndexAdorner.TextBlock.FontStyle,
				UpperIndexAdorner.TextBlock.FontWeight,
				UpperIndexAdorner.TextBlock.TextDecorations
				);


			if (fontChooser.ShowDialog() != true) return;

			OnMenuOpened(sender, new MenuChangesEventArgs(this));
			Font.ApplyFont(fontChooser, UpperIndexAdorner.TextBlock);
			OnMenuClosed(sender, new MenuChangesEventArgs(this));
		}

		private void LowerIndexFontBtn_Click(object sender, RoutedEventArgs e)
		{
			var fontChooser = new FontChooser();
			Font.ApplyFont(fontChooser,
				LowerIndexAdorner.TextBlock.Foreground,
				LowerIndexAdorner.TextBlock.FontFamily,
				LowerIndexAdorner.TextBlock.FontSize,
				LowerIndexAdorner.TextBlock.FontStretch,
				LowerIndexAdorner.TextBlock.FontStyle,
				LowerIndexAdorner.TextBlock.FontWeight,
				LowerIndexAdorner.TextBlock.TextDecorations
				);


			if (fontChooser.ShowDialog() != true) return;

			OnMenuOpened(sender, new MenuChangesEventArgs(this));
			Font.ApplyFont(fontChooser, LowerIndexAdorner.TextBlock);
			OnMenuClosed(sender, new MenuChangesEventArgs(this));
		}

		protected override void OnVisualParentChanged(DependencyObject oldParent)
		{
			base.OnVisualParentChanged(oldParent);
			if (UpperIndexAdorner != null)
			{
				if (oldParent != null)
					((Canvas)oldParent).Children.Remove(UpperIndexAdorner);

				if (Parent != null && Parent != oldParent)
					((Canvas)Parent).Children.Add(UpperIndexAdorner);
			}
			if (LowerIndexAdorner != null)
			{
				if (oldParent != null)
					((Canvas)oldParent).Children.Remove(LowerIndexAdorner);

				if (Parent != null && Parent != oldParent)
					((Canvas)Parent).Children.Add(LowerIndexAdorner);
			}
		}

		private void ContextMenu_Closed(object sender, RoutedEventArgs e)
		{
			UpperIndexAdorner.TextBlock.Text = UpperIndexBox.Text;
			LowerIndexAdorner.TextBlock.Text = LowerIndexBox.Text;
			OnMenuClosed(sender, new MenuChangesEventArgs(this));
		}

		private void ContextMenu_Opened(object sender, RoutedEventArgs e)
		{
			UpperIndexBox.Text = UpperIndexAdorner.TextBlock.Text;
			LowerIndexBox.Text = LowerIndexAdorner.TextBlock.Text;
			OnMenuOpened(sender, new MenuChangesEventArgs(this));
		}

		public void InitializeSelectionAdorners()
		{
			var adornerLayer = AdornerLayer.GetAdornerLayer(TextBox);
			var adorner = new EllipseAdorner(TextBox);

			var binding = new Binding("IsSelected");
			binding.Source = this;

			adorner.SetBinding(EllipseAdorner.IsActiveProperty, binding);
			adornerLayer.Add(adorner);
		}

		private void TextBox_OnLoaded(object sender, RoutedEventArgs e)
		{
			InitializeSelectionAdorners();
		}

		public Dictionary<string, object> MakeSnapshot()
		{
			var snapshot = new Dictionary<string, object>();
			snapshot.Add("UpperAdornerText", UpperIndexAdorner.TextBlock.Text);
			snapshot.Add("UpperIndexFont", Font.ExtractFont(UpperIndexAdorner.TextBlock));
			snapshot.Add("LowerAdornerText", LowerIndexAdorner.TextBlock.Text);
			snapshot.Add("LowerIndexFont", Font.ExtractFont(LowerIndexAdorner.TextBlock));

			return snapshot;
		}

		public void RestoreSnapshop(Dictionary<string, object> snapshot)
		{
			if (snapshot.ContainsKey("UpperAdornerText"))
				UpperIndexAdorner.TextBlock.Text = (string)snapshot["UpperAdornerText"];
			if (snapshot.ContainsKey("LowerAdornerText"))
				LowerIndexAdorner.TextBlock.Text = (string)snapshot["LowerAdornerText"];
			if (snapshot.ContainsKey("UpperIndexFont"))
				Font.ApplyFont(UpperIndexAdorner.TextBlock, (Font)snapshot["UpperIndexFont"]);
			if (snapshot.ContainsKey("LowerIndexFont"))
				Font.ApplyFont(LowerIndexAdorner.TextBlock, (Font)snapshot["LowerIndexFont"]);
		}

		public bool DetectChanges(Dictionary<string, object> snapshot)
		{
			if (snapshot.ContainsKey("UpperAdornerText") &&
			    UpperIndexAdorner.TextBlock.Text != (string) snapshot["UpperAdornerText"])
				return true;
			if (snapshot.ContainsKey("LowerAdornerText") &&
				LowerIndexAdorner.TextBlock.Text != (string)snapshot["LowerAdornerText"])
				return true;
			if (snapshot.ContainsKey("UpperIndexFont") &&
			    !Font.ExtractFont(UpperIndexAdorner.TextBlock).Equals((Font) snapshot["UpperIndexFont"]))
				return true;
			if (snapshot.ContainsKey("LowerIndexFont") &&
				!Font.ExtractFont(LowerIndexAdorner.TextBlock).Equals((Font)snapshot["LowerIndexFont"]))
				return true;
			return false;
		}

		public event Action<object, MenuChangesEventArgs> MenuOpened;

		protected virtual void OnMenuOpened(object sender, MenuChangesEventArgs e)
		{
			Action<object, MenuChangesEventArgs> handler = MenuOpened;
			if (handler != null) handler(sender, e);
		}

		public event Action<object, MenuChangesEventArgs> MenuClosed;

		protected virtual void OnMenuClosed(object sender, MenuChangesEventArgs e)
		{
			Action<object, MenuChangesEventArgs> handler = MenuClosed;
			if (handler != null) handler(sender, e);
		}

		private void TextBox_OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
		{
			if (!_textWasChanged)
			{
				TextBox.Text = "Enter the text";
				_textWasChanged = false;
				return;
			}

			string newText = TextBox.Text;
			string oldText = _previousText;
			if (newText == oldText) return;

			OnTextChanged(sender, new NodeTextChangedEventArgs(this, oldText, newText));

			_previousText = newText;
		}

		private void TextBox_OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
		{
			if (!_textWasChanged)
			{
				TextBox.Text = "";
				_textWasChanged = false;
			}
		}
	}
}
